home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Dev / SmallTalk / mstlex.c < prev    next >
C/C++ Source or Header  |  1995-08-25  |  31KB  |  1,297 lines

  1. /***********************************************************************
  2.  *
  3.  *    Lexer Module.
  4.  *
  5.  ***********************************************************************/
  6.  
  7. /***********************************************************************
  8.  *
  9.  * Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
  10.  * Written by Steve Byrne.
  11.  *
  12.  * This file is part of GNU Smalltalk.
  13.  *
  14.  * GNU Smalltalk is free software; you can redistribute it and/or modify it
  15.  * under the terms of the GNU General Public License as published by the Free
  16.  * Software Foundation; either version 1, or (at your option) any later 
  17.  * version.
  18.  * 
  19.  * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
  20.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  21.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  22.  * more details.
  23.  * 
  24.  * You should have received a copy of the GNU General Public License along with
  25.  * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
  26.  * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
  27.  *
  28.  ***********************************************************************/
  29.  
  30. /*
  31.  *    Change Log
  32.  * ============================================================================
  33.  * Author      Date       Change 
  34.  * sbb         31 Dec 91      Began adding support for having a changes file
  35.  *              and pointing methods to that instead of
  36.  *              the actual source file (which can get out of sync,
  37.  *              and cause recompiles to lose).
  38.  *
  39.  * sbb         29 Dec 91      Added support for readline's conditional .inputrc
  40.  *              definitions, keyed off of "Smalltalk".
  41.  *
  42.  * sbb         29 Nov 91      Added fileNameOOP to hold full path name for files,
  43.  *              so that all methods share the same file name string.
  44.  *              Also, adjusted getCurFileName to return the full path
  45.  *              name.
  46.  *
  47.  * sbb          5 Jul 91      added setStreamInfo so that stuff filed in from Emacs
  48.  *              can have more accurate information such as the line
  49.  *              number in the buffer instead of the line number in
  50.  *              the temporary file.
  51.  *
  52.  * sbb         25 Mar 91      Added -> operator.
  53.  *
  54.  * sbb         24 Mar 91      Fixed lexing of foo:= to be seen as foo :=.
  55.  *
  56.  * sbyrne    24 Apr 90      Error checking for integers too large.
  57.  *
  58.  * sbyrne    20 Apr 90      Added the closeIt argument to popStream so that the
  59.  *              closing behavior could be separated from the popping
  60.  *              behavior (in particular, for fileIn).
  61.  *
  62.  * sbyrne     7 Apr 90      Character lexing routines (such as nextChar) now
  63.  *              return ints to get around problems on implementations
  64.  *              that don't sign extend characters by default.
  65.  *
  66.  * sbyrne    15 Feb 90      Added support for := as alternative assignment
  67.  *              operator.
  68.  *
  69.  * sbyrne     3 Sep 89      added getCurFileName
  70.  *
  71.  * sbyrne    30 Aug 89      Fixed a bug in parseIdent which was parsing foo:2
  72.  *              (note no space) not as foo: and 2, but as a mixed up
  73.  *              token.
  74.  *
  75.  * sbyrne     8 Jul 89      Added prompt when input is a terminal.  This should
  76.  *              help Emacs's shell mode determine what has been typed
  77.  *              as input to the system.
  78.  *
  79.  * sbyrne    24 Jan 89      Added 2 chars of push back, because 3. needs to look
  80.  *              ahead one more character to see if its 3.DIGIT or 3.
  81.  *              next statement.
  82.  *
  83.  * sbyrne    27 Dec 88    Created.
  84.  */
  85.  
  86.  
  87. #include "mst.h"
  88. #include "mstlex.h"
  89. #include "mst.tab.h"
  90. #include "mststr.h"
  91. #include "mstid.h"
  92. #include "mstdict.h"
  93. #include "mstcomp.h"
  94. #include "msttree.h"
  95. #include "mstsysdep.h"
  96. #include <stdio.h>
  97. #include <math.h>
  98. #ifdef USE_READLINE
  99. #include <readline/readline.h>
  100. #endif /* USE_READLINE */
  101.  
  102. #define WHITE_SPACE        1
  103. #define DIGIT            2
  104. #define ID_CHAR            4
  105. #define SPECIAL_CHAR        8
  106.  
  107. #define ERROR_ARGS        arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8
  108.  
  109. #define EMACS_PROCESS_MARKER    '\001' /* ^A as marker -- random choice */
  110.  
  111. #define CHANGE_FILE_NAME    "st-changes.st"
  112.  
  113. typedef struct StringStreamStruct {
  114.   char        *strBase;    /* base of asciz string */
  115.   char        *str;        /* pointer into asciz string */
  116. } StringStream;
  117.  
  118. struct StreamStruct {
  119.   StreamType    type;
  120.   int        pushedBackChars[2]; /* holds the 2 characters of buffering */
  121.   int        pushedBackCount; /* number of chars pushed back */
  122.   int        line;
  123.   int        column;
  124.   char        *fileName;
  125.   int        fileOffset;
  126.   OOP        fileNameOOP;    /* holds full path name for file as an oop */
  127.   Boolean    prompt;
  128.   union {
  129.     FILE    *u_st_file;
  130.     StringStream u_st_str;
  131.   } st;
  132.   Stream    prevStream;
  133. };
  134.  
  135. #define st_file        st.u_st_file
  136. #define st_str        st.u_st_str
  137.  
  138. extern char    *strdup();
  139.  
  140.  
  141. Stream    inStream = NULL;
  142. int    lexDebug;
  143. char    *processingFile = nil;
  144.  
  145. Boolean        reportErrors = true;
  146. char        *firstErrorStr = NULL;
  147. char        *firstErrorFile = NULL;
  148. long        firstErrorLine;
  149.  
  150. static FILE    *changeStr;
  151.  
  152. static int            byteAddr, methodStartPos = 0;
  153. static Boolean            produceInternalToken;
  154.  
  155. static Boolean            isDigit(), isBaseDigit(), parsePrimitive(),
  156.                 parseDigits(), parseFraction();
  157. static int            illegal(), comment(), charLiteral(),
  158.                 parseBinOP(), stringLiteral(), parseNumber(),
  159.                 parseIdent(), myGetC(), parseColon(),
  160.                   nextChar();
  161. static char            *scanStringoid();
  162. static void             unreadChar(), lineStamp(), myUngetC(), 
  163.                 myClose();
  164. static Stream            pushNewStream();
  165.  
  166. typedef struct {
  167.   int        (*lexFunc)();
  168.   int        retToken;
  169.   int        charClass;
  170. } LexTabElt;
  171.  
  172. static LexTabElt charTab[128] = {
  173. /*   0 */    illegal,    0,    0,
  174. /*   1 */    illegal,    0,    0,
  175. /*   2 */    illegal,    0,    0,
  176. /*   3 */    illegal,    0,    0,
  177. /*   4 */    illegal,    0,    0,
  178. /*   5 */    illegal,    0,    0,
  179. /*   6 */    illegal,    0,    0,
  180. /*   7 */    illegal,    0,    0,
  181. /*   8 */    illegal,    0,    0,
  182. /*   9 */    0,        0,    WHITE_SPACE,
  183. /*  10 */    0,        0,    WHITE_SPACE,
  184. /*  11 */    illegal,    0,    0,
  185. /*  12 */    0,        0,    WHITE_SPACE, 
  186. /*  13 */    0,        0,    WHITE_SPACE,
  187. /*  14 */    illegal,    0,    0,    
  188. /*  15 */    illegal,    0,    0,
  189. /*  16 */    illegal,    0,    0,
  190. /*  17 */    illegal,    0,    0,
  191. /*  18 */    illegal,    0,    0,
  192. /*  19 */    illegal,    0,    0,
  193. /*  20 */    illegal,    0,    0,
  194. /*  21 */    illegal,    0,    0,
  195. /*  22 */    illegal,    0,    0,
  196. /*  23 */    illegal,    0,    0,
  197. /*  24 */    illegal,    0,    0,
  198. /*  25 */    illegal,    0,    0,
  199. /*  26 */    illegal,    0,    0,
  200. /*  27 */    illegal,    0,    0,
  201. /*  28 */    illegal,    0,    0,
  202. /*  29 */    illegal,    0,    0,
  203. /*  30 */    illegal,    0,    0,
  204. /*  31 */    illegal,    0,    0,
  205. /*     */    0,        0,    WHITE_SPACE,
  206. /*   ! */    parseBinOP,    0,    SPECIAL_CHAR, 
  207. /*   " */    comment,    0,    0,
  208. /*   # */    0,        SHARP,    0,
  209. /*   $ */    charLiteral,    0,    0,
  210. /*   % */    parseBinOP,    0,    SPECIAL_CHAR,
  211. /*   & */    parseBinOP,    0,    SPECIAL_CHAR,
  212. /*   ' */    stringLiteral,    0,    0,
  213. /*   ( */    0,        OPEN_PAREN, 0,
  214. /*   ) */    0,        CLOSE_PAREN, 0,
  215. /*   * */    parseBinOP,    0,    SPECIAL_CHAR,
  216. /*   + */    parseBinOP,    0,    SPECIAL_CHAR,
  217. /*   , */    parseBinOP,    0,    SPECIAL_CHAR,
  218. /*   - */    parseBinOP,    0,    SPECIAL_CHAR,
  219. /*   . */    0,        DOT,    0,
  220. /*   / */    parseBinOP,    0,    SPECIAL_CHAR,
  221. /*   0 */    parseNumber,    0,    DIGIT | ID_CHAR,
  222. /*   1 */    parseNumber,    0,    DIGIT | ID_CHAR,
  223. /*   2 */    parseNumber,    0,    DIGIT | ID_CHAR,
  224. /*   3 */    parseNumber,    0,    DIGIT | ID_CHAR,
  225. /*   4 */    parseNumber,    0,    DIGIT | ID_CHAR,
  226. /*   5 */    parseNumber,    0,    DIGIT | ID_CHAR,
  227. /*   6 */    parseNumber,    0,    DIGIT | ID_CHAR,
  228. /*   7 */    parseNumber,    0,    DIGIT | ID_CHAR,
  229. /*   8 */    parseNumber,    0,    DIGIT | ID_CHAR,
  230. /*   9 */    parseNumber,    0,    DIGIT | ID_CHAR,
  231. /*   : */    parseColon,    0,    0,
  232. /*   ; */    0,        SEMICOLON, 0,
  233. /*   < */    parseBinOP,    0,    SPECIAL_CHAR,
  234. /*   = */    parseBinOP,    0,    SPECIAL_CHAR,
  235. /*   > */    parseBinOP,    0,    SPECIAL_CHAR,
  236. /*   ? */    parseBinOP,    0,    SPECIAL_CHAR,
  237. /*   @ */    parseBinOP,    0,    SPECIAL_CHAR,
  238. /*   A */    parseIdent,    0,    ID_CHAR,
  239. /*   B */    parseIdent,    0,    ID_CHAR,
  240. /*   C */    parseIdent,    0,    ID_CHAR,
  241. /*   D */    parseIdent,    0,    ID_CHAR,
  242. /*   E */    parseIdent,    0,    ID_CHAR,
  243. /*   F */    parseIdent,    0,    ID_CHAR,
  244. /*   G */    parseIdent,    0,    ID_CHAR,
  245. /*   H */    parseIdent,    0,    ID_CHAR,
  246. /*   I */    parseIdent,    0,    ID_CHAR,
  247. /*   J */    parseIdent,    0,    ID_CHAR,
  248. /*   K */    parseIdent,    0,    ID_CHAR,
  249. /*   L */    parseIdent,    0,    ID_CHAR,
  250. /*   M */    parseIdent,    0,    ID_CHAR,
  251. /*   N */    parseIdent,    0,    ID_CHAR,
  252. /*   O */    parseIdent,    0,    ID_CHAR,
  253. /*   P */    parseIdent,    0,    ID_CHAR,
  254. /*   Q */    parseIdent,    0,    ID_CHAR,
  255. /*   R */    parseIdent,    0,    ID_CHAR,
  256. /*   S */    parseIdent,    0,    ID_CHAR,
  257. /*   T */    parseIdent,    0,    ID_CHAR,
  258. /*   U */    parseIdent,    0,    ID_CHAR,
  259. /*   V */    parseIdent,    0,    ID_CHAR,
  260. /*   W */    parseIdent,    0,    ID_CHAR,
  261. /*   X */    parseIdent,    0,    ID_CHAR,
  262. /*   Y */    parseIdent,    0,    ID_CHAR,
  263. /*   Z */    parseIdent,    0,    ID_CHAR,
  264. /*   [ */    0,        OPEN_BRACKET, 0,
  265. /*   \ */    parseBinOP,    0,    SPECIAL_CHAR,
  266. /*   ] */    0,        CLOSE_BRACKET, 0,
  267. /*   ^ */    0,        UPARROW, 0,
  268. /*   _ */    0,        ASSIGN,    0,
  269. /*   ` */    illegal,    0,    0,
  270. /*   a */    parseIdent,    0,    ID_CHAR,
  271. /*   b */    parseIdent,    0,    ID_CHAR,
  272. /*   c */    parseIdent,    0,    ID_CHAR,
  273. /*   d */    parseIdent,    0,    ID_CHAR,
  274. /*   e */    parseIdent,    0,    ID_CHAR,
  275. /*   f */    parseIdent,    0,    ID_CHAR,
  276. /*   g */    parseIdent,    0,    ID_CHAR,
  277. /*   h */    parseIdent,    0,    ID_CHAR,
  278. /*   i */    parseIdent,    0,    ID_CHAR,
  279. /*   j */    parseIdent,    0,    ID_CHAR,
  280. /*   k */    parseIdent,    0,    ID_CHAR,
  281. /*   l */    parseIdent,    0,    ID_CHAR,
  282. /*   m */    parseIdent,    0,    ID_CHAR,
  283. /*   n */    parseIdent,    0,    ID_CHAR,
  284. /*   o */    parseIdent,    0,    ID_CHAR,
  285. /*   p */    parseIdent,    0,    ID_CHAR,
  286. /*   q */    parseIdent,    0,    ID_CHAR,
  287. /*   r */    parseIdent,    0,    ID_CHAR,
  288. /*   s */    parseIdent,    0,    ID_CHAR,
  289. /*   t */    parseIdent,    0,    ID_CHAR,
  290. /*   u */    parseIdent,    0,    ID_CHAR,
  291. /*   v */    parseIdent,    0,    ID_CHAR,
  292. /*   w */    parseIdent,    0,    ID_CHAR,
  293. /*   x */    parseIdent,    0,    ID_CHAR,
  294. /*   y */    parseIdent,    0,    ID_CHAR,
  295. /*   z */    parseIdent,    0,    ID_CHAR,
  296. /*   { */    illegal,    0,    0,
  297. /*   | */    parseBinOP,    0,    SPECIAL_CHAR,
  298. /*   } */    illegal,    0,    0,
  299. /*   ~ */    parseBinOP,    0,    SPECIAL_CHAR,
  300. /*  ^? */    illegal,    0,    0
  301. };
  302.  
  303.  
  304. int yylex(lvalp, llocp)
  305. YYSTYPE *lvalp;
  306. voidPtr *llocp;        /* Bison is broken, doesn define type YYLTYPE!! */
  307. {
  308.   int        ic, result, tokenStartPos;
  309.  
  310.   if (produceInternalToken) {
  311.     /* The internal token is a trick to make the grammar be "conditional".
  312.      * Normally, the allowable syntax for internal compilation is that of
  313.      * a single method (without the terminating BANG).  However, would make
  314.      * for an ambiguous grammar since a method declaration could look like
  315.      * an immediate expression.  So, when the compiler is called internally,
  316.      * we force the first token returned by the lexer to be INTERNAL_TOKEN
  317.      * and make the top most production in the grammar use INTERNAL_TOKEN as
  318.      * the first token of an internal method. */
  319.     produceInternalToken = false;
  320.     return (INTERNAL_TOKEN);
  321.   }
  322.  
  323.   while ((ic = nextChar()) != EOF) {
  324.     if ((charTab[ic].charClass & WHITE_SPACE) == 0) {
  325.       if (methodStartPos < 0) {
  326.     tokenStartPos = getCurFilePos();
  327.       }
  328.  
  329.       if (charTab[ic].lexFunc) {
  330.     result = (*charTab[ic].lexFunc)(ic, lvalp);
  331.     if (result) {
  332.       if (methodStartPos < 0) { /* only do this here to ignore comments */
  333.         methodStartPos = tokenStartPos - 1;
  334.       }
  335.       return (result);
  336.     }
  337.       } else if (charTab[ic].retToken) {
  338.     return (charTab[ic].retToken);
  339.       } else {
  340.     errorf("Unknown character %d in input stream, ignoring", ic);
  341.     hadError = true;
  342.       }
  343.     }
  344.   }
  345.   return (0);            /* keep fussy compilers happy */
  346. }
  347.  
  348. initLexer(calledInternally)
  349. Boolean calledInternally;
  350. {
  351.   byteAddr = 0;
  352.   
  353.   produceInternalToken = calledInternally;
  354. }
  355.  
  356. static int illegal(c, lvalp)
  357. char    c;
  358. YYSTYPE *lvalp;
  359. {
  360.   char        charName[3], *cp;
  361.  
  362.   cp = charName;
  363.  
  364.   if (c < ' ' || c > '~') {
  365.     *cp++ = '^';
  366.     c &= 127;            /* strip high bit */
  367.     c ^= 64;            /* uncontrolify */
  368.   }
  369.  
  370.   *cp++ = c;
  371.   *cp++ = '\0';
  372.   
  373.   errorf("Illegal character %s", charName);
  374.   hadError = true;
  375. }
  376.  
  377.  
  378. /*
  379.  *    static int comment(c, lvalp)
  380.  *
  381.  * Description
  382.  *
  383.  *    Scan a comment, but return 0 to indicate to the lexer that it's to be
  384.  *    ignored (since it is a comment).
  385.  *
  386.  * Inputs
  387.  *
  388.  *    c     : first character of the comment (the delimiter).  Not terribly
  389.  *        useful, but it's what the lexer calls us with.
  390.  *    lvalp : ignored...passed because we're invoked indirectly and some of
  391.  *        the functions that could be called require this parameter.
  392.  *
  393.  * Outputs
  394.  *
  395.  *    0, which tells the lexer to ignore this token.
  396.  */
  397. static int comment(c, lvalp)
  398. char    c;
  399. YYSTYPE *lvalp;
  400. {
  401.   scanStringoid(c, "comment");
  402.  
  403.   return (0);
  404. }
  405.  
  406. /*
  407.  *    static int charLiteral(c, lvalp)
  408.  *
  409.  * Description
  410.  *
  411.  *    parse a character literal.
  412.  *
  413.  * Inputs
  414.  *
  415.  *    c     : input character -- ignored
  416.  *    lvalp : ignored -- passed because we're called indirectly.
  417.  *
  418.  * Outputs
  419.  *
  420.  *    CHAR_LITERAL token normally; 0 on EOF.
  421.  */
  422. static int charLiteral(c, lvalp)
  423. char    c;
  424. YYSTYPE *lvalp;
  425. {
  426.   int        ic;
  427.  
  428.   ic = nextChar();
  429.   if (ic == EOF) {
  430.     errorf("Unterminated character literal, attempting recovery");
  431.     unreadChar(ic);
  432.     hadError = true;
  433.     return (0);
  434.   } else {
  435.     lvalp->cval = ic;
  436.     return (CHAR_LITERAL);
  437.   }
  438. }
  439.  
  440. static int parseColon(c, lvalp)
  441. char    c;
  442. YYSTYPE *lvalp;
  443. {
  444.   int        ic;
  445.  
  446.   ic = nextChar();
  447.   if (ic == '=') {        /* parse :=, compatibility mode assign */
  448.     return (ASSIGN);
  449.   } else {
  450.     unreadChar(ic);
  451.   }
  452.  
  453.   return (COLON);
  454. }
  455.  
  456.  
  457. static int parseBinOP(c, lvalp)
  458. char    c;
  459. YYSTYPE *lvalp;
  460. {
  461.   char        buf[3], *bp;
  462.   int        ic;
  463.  
  464.   bp = buf;
  465.   *bp++ = c;
  466.  
  467.   ic = nextChar();
  468.   if (c == '<') {
  469.     if (ic == 'p') {
  470.       if (parsePrimitive(ic, lvalp)) {
  471.     return (PRIMITIVE_START);
  472.       }
  473.     }
  474.   }
  475.  
  476.   if (ic != EOF && (charTab[ic].charClass & SPECIAL_CHAR)) {
  477.     *bp++ = ic;            /* accumulate next char */
  478.   } else {
  479.     unreadChar(ic);
  480.     if (c == '-' && isDigit(ic)) {
  481.       return (parseNumber('-', lvalp));
  482.     }
  483.   }
  484. #ifdef old_code /* Fri Apr 19 20:37:57 1991 */
  485. /**/  if (c == '-') {
  486. /**/    if (ic == '>') {        /* -> */
  487. /**/      *bp++ = ic;
  488. /**/    } else {
  489. /**/      unreadChar(ic);
  490. /**/      if (isDigit(ic)) {
  491. /**/    return (parseNumber('-', lvalp));
  492. /**/      }
  493. /**/    }
  494. /**/  } else {
  495. /**/    if (ic != EOF && (charTab[ic].charClass & SPECIAL_CHAR)) {
  496. /**/      *bp++ = ic;
  497. /**/    } else {
  498. /**/      unreadChar(ic);
  499. /**/    }
  500. /**/  }
  501. #endif /* old_code Fri Apr 19 20:37:57 1991 */
  502.  
  503.   *bp = '\0';
  504.  
  505.   if (strcmp(buf, "!") == 0) {
  506.     /* technically, token BANG has no string value, it's just a simple token,
  507.        so we return from here before we set the token's value */
  508.     return (BANG);
  509.   } else if (strcmp(buf, "!!") == 0) {
  510.     unreadChar('!');
  511.     return (BANG);
  512.   }
  513.   
  514.   lvalp->sval = copyStr(buf);
  515.   
  516.   if (strcmp(buf, "|") == 0) {
  517.     return (VERTICAL_BAR);
  518.   } else {
  519.     return (BINOP);
  520.   }
  521. }
  522.  
  523. static int stringLiteral(c, lvalp)
  524. char    c;
  525. YYSTYPE *lvalp;
  526. {
  527.   lvalp->sval = copyStr(scanStringoid(c, "string"));
  528.   return (STRING_LITERAL);
  529. }
  530.  
  531. static Boolean parsePrimitive(c, lvalp)
  532. char    c;
  533. YYSTYPE *lvalp;
  534. {
  535.   Boolean     result;
  536.  
  537.   parseIdent(c, lvalp);
  538.   result = (strcmp(lvalp->sval, "primitive:") == 0);
  539.   free(lvalp->sval);
  540.   return (result);
  541. }
  542.  
  543. static int parseIdent(c, lvalp)
  544. char    c;
  545. YYSTYPE *lvalp;
  546. {
  547.   int        ic, identType;
  548.   
  549.   initIdent(c);
  550.   
  551.   identType = IDENTIFIER;
  552.   
  553.   for (;;) {
  554.     while (((ic = nextChar()) != EOF) && (charTab[ic].charClass & ID_CHAR)) {
  555.       addIdentChar(ic);
  556.     }
  557.     
  558.     if (ic == ':') {        /* we have a keyword if id ends with : */
  559.       ic = nextChar();
  560.       unreadChar(ic);
  561.       if (ic == '=') {        /* except if we have 'foo:=' => 'foo :=' */
  562.     if (identType != IDENTIFIER) {
  563.       errorf("Malformed symbol literal");
  564.       break;
  565.     }
  566.     unreadChar(':');
  567.     break;
  568.       }
  569.       addIdentChar(':');
  570.       if (ic == EOF || (charTab[ic].charClass & ID_CHAR) == 0
  571.       || (charTab[ic].charClass & DIGIT) != 0) {
  572.     if (identType == IDENTIFIER) {
  573.       /* first time through */
  574.       identType = KEYWORD;
  575.     }
  576.     break;
  577.       }
  578.       identType = SYMBOL_KEYWORD;
  579.     } else {
  580.       unreadChar(ic);
  581.       break;
  582.     }
  583.   }
  584.   
  585.   lvalp->sval = copyStr(doneIdent());
  586.   
  587.   return (identType);
  588. }
  589.  
  590. static int parseNumber(c, lvalp)
  591. char    c;
  592. YYSTYPE *lvalp;
  593. {
  594.   int        base, exponent, ic;
  595.   double    num, floatExponent;
  596.   Boolean    mantissaParsed = false, isNegative = false, dotSeen = false;
  597.   double    scale;
  598.   
  599.   base = 10;
  600.   exponent = 0;
  601.   scale = 0.0;
  602.   ic = c;
  603.  
  604.   if (ic != '-') {
  605.     parseDigits(ic, 10, &num);
  606.     ic = nextChar();
  607.     if (ic == 'r') {
  608.       base = num;
  609.       ic = nextChar();
  610.     } else {
  611.       mantissaParsed = true;
  612.     }
  613.   }
  614.  
  615.   /*
  616.    * here we've either
  617.    *  a) parsed base, an 'r' and are sitting on the following character
  618.    *  b) parsed the integer part of the mantissa, and are sitting on the char
  619.    *     following it, or
  620.    *  c) parsed nothing and are sitting on a - sign.
  621.    */
  622.   if (!mantissaParsed) {
  623.     if (ic == '-') {
  624.       isNegative = true;
  625.       ic = nextChar();
  626.     }
  627.     
  628.     parseDigits(ic, base, &num);
  629.     ic = nextChar();
  630.   }
  631.  
  632.   if (ic == '.') {
  633.     ic = nextChar();
  634.     if (!isDigit(ic)) {
  635.       /* OOPS...we gobbled the '.' by mistake...it was a statement boundary
  636.      delimiter.  We have an integer that we need to return, and need to
  637.      push back both the . and the character that we just read. */
  638.       unreadChar(ic);
  639.       ic = '.';
  640.     } else {
  641.       dotSeen = true;
  642.       parseFraction(ic, base, &num, &scale);
  643.       ic = nextChar();
  644.     }
  645.   }
  646.  
  647.   if (ic == 'e') {
  648.     ic = nextChar();
  649.     if (ic == '-') {
  650.       parseDigits(nextChar(), 10, &floatExponent);
  651.       exponent -= floatExponent;
  652.     } else {
  653.       parseDigits(ic, 10, &floatExponent);
  654.       exponent += floatExponent;
  655.     }
  656.   } else {
  657.     unreadChar(ic);
  658.   }
  659.  
  660.   if (scale != 0.0) {
  661.     num *= scale/*pow((double)base, (double)exponent)*/;
  662.   }
  663.  
  664.   if (exponent != 0) {
  665.     num *= pow((double)base, (double)exponent);
  666.   }
  667.  
  668.   if (isNegative) {
  669.     num = -num;
  670.   }
  671.     
  672.   if (dotSeen) {
  673.     lvalp->fval = num;
  674.     return (FLOATING_LITERAL);
  675.   } else {
  676.     lvalp->ival = num;
  677.     if (num < -(1<<30) || num >= (1 << 30)) {
  678.       /* at least warn the guy... */
  679.       errorf("Integer literal %d too large to be represented in Smalltalk",
  680.          num);
  681.       hadError = true;
  682.     }
  683.       
  684.     return (INTEGER_LITERAL);
  685.   }
  686. }
  687.  
  688. static Boolean parseDigits(c, base, numPtr)
  689. char    c;
  690. int    base;
  691. double    *numPtr;
  692. {
  693.   double    result;
  694.   Boolean    oneDigit = false;
  695.  
  696.   for (result = 0.0; isBaseDigit(c, base); c = nextChar()) {
  697.     result *= base;
  698.     oneDigit = true;
  699.     result += digitToInt(c, base);
  700.   }
  701.  
  702.   if (!oneDigit) {
  703.     errorf("Unexpected EOF while scanning number");
  704.     hadError = true;
  705.   }
  706.  
  707.   unreadChar(c);
  708.  
  709.   *numPtr = result;
  710.  
  711.   return (true);
  712. }
  713.  
  714. static Boolean parseFraction(c, base, numPtr, scalePtr)
  715. char    c;
  716. int    base;
  717. double    *numPtr, *scalePtr;
  718. {
  719.   double    scale;
  720.   double    num;
  721.  
  722.   scale = 1.0;
  723.  
  724.   for (num = *numPtr; isBaseDigit(c, base); c = nextChar()) {
  725.     num *= base;
  726.     num += digitToInt(c, base);
  727.     scale /= base;
  728.   }
  729.  
  730.   unreadChar(c);
  731.  
  732.   *numPtr = num;
  733.   *scalePtr = scale;
  734.  
  735.   return (true);
  736. }
  737.  
  738.  
  739. int digitToInt(c, base)
  740. char    c;
  741. int    base;
  742. {
  743.   if (c < '0' || (c > '9' && c < 'A') || c > 'Z') {
  744.     errorf("Illegal digit %c in number", c);
  745.     hadError = true;
  746.     return (0);
  747.   }
  748.  
  749.   if (c >= 'A') {
  750.     c = c - 'A' + 10;
  751.   } else {
  752.     c -= '0';
  753.   }
  754.  
  755.   if (c >= base) {
  756.     errorf("Digit '%c' too large for base %d", c, base);
  757.     hadError = true;
  758.     return (0);
  759.   }
  760.  
  761.   return (c);
  762. }
  763.  
  764. static Boolean isBaseDigit(c, base)
  765. char    c;
  766. int    base;
  767. {
  768.   if (c < '0' || (c > '9' && c < 'A') || c > 'Z') {
  769.     return (false);
  770.   }
  771.   
  772.   if (c >= 'A') {
  773.     c = c - 'A' + 10;
  774.   } else {
  775.     c -= '0';
  776.   }
  777.  
  778.   return (c < base);
  779. }
  780.  
  781.  
  782. static Boolean isDigit(c)
  783. char    c;
  784. {
  785.   return ((charTab[c].charClass & DIGIT) != 0);
  786. }
  787.  
  788. static char *scanStringoid(startChar, typeName)
  789. char    startChar, *typeName;
  790. {
  791.   int        ic;
  792.  
  793.   initStrBuf();
  794.  
  795.   for (;;) {
  796.     ic = nextChar();
  797.     if (ic == EOF) {
  798.       errorf("Unterminated %s, attempting recovery", typeName);
  799.       hadError = true;
  800.       return (curStrBuf());
  801.     }
  802.     if (ic == startChar) {
  803.       /* check for doubled delimiters */
  804.       ic = nextChar();
  805.       if (ic != startChar) {
  806.     unreadChar(ic);
  807.     return (curStrBuf());
  808.       }
  809.     }
  810.     addStrBufChar(ic);
  811.   }
  812.   
  813. }
  814.  
  815. /*
  816.  * Report an error to the user.  ### Will show position in the text of
  817.  * the error at some point.
  818.  */
  819. /*VARARGS1*/
  820. errorf(str, ERROR_ARGS)
  821. char    *str;
  822. int    ERROR_ARGS;
  823. {
  824.   char        errStr[256];
  825.  
  826.   if (reportErrors) {
  827.     fflush(stdout);
  828.   }
  829.   lineStamp();
  830.   if (reportErrors) {
  831.     fprintf(stderr, str, ERROR_ARGS);
  832.     fprintf(stderr, "\n");
  833.     fflush(stderr);
  834.   } else {
  835.     if (firstErrorStr == NULL) {
  836.       sprintf(errStr, str, ERROR_ARGS);
  837.       firstErrorStr = strdup(errStr);
  838.     }
  839.   }
  840. }
  841.  
  842.  
  843. yyerror(s)
  844. char    *s;
  845. {
  846.   errorf("%s", s);
  847. #ifdef old_code /* Sat Feb  8 17:14:18 1992 */
  848. /**/  lineStamp();
  849. /**/  fprintf(stderr, "%s\n", s);
  850. #endif /* old_code Sat Feb  8 17:14:18 1992 */
  851. }
  852.  
  853. static void lineStamp()
  854. {
  855.   if (reportErrors) {
  856.     if (inStream) {
  857.       if (inStream->fileName) {
  858.     fprintf(stderr, "\"%s\", ", inStream->fileName);
  859.       }
  860.       fprintf(stderr, "line %d: ", inStream->line);
  861.     } else {
  862.       fprintf(stderr, "<unknown position> ");
  863.     }
  864.   } else {            /* called internally with error handling */
  865.     if (inStream) {
  866.       if (inStream->fileName) {
  867.     if (firstErrorStr == NULL) {
  868.       firstErrorFile = strdup(inStream->fileName);
  869.     }
  870.       }
  871.       if (firstErrorStr == NULL) {
  872.     firstErrorLine = inStream->line;
  873.       }
  874.     } else {
  875.       if (firstErrorStr == NULL) {
  876.     firstErrorLine = -1;
  877.       }
  878.     }
  879.   }
  880. }
  881.  
  882. StreamType getCurStreamType()
  883. {
  884.   if (inStream) {
  885.     return (inStream->type);
  886.   } else {
  887.     return (unknownStreamType);
  888.   }
  889. }
  890.  
  891. OOP getCurString()
  892. {
  893.   if (inStream && inStream->type == stringStreamType) {
  894.     return (stringNew(inStream->st_str.strBase));
  895.   } else {
  896.     return (nilOOP);
  897.   }
  898. }
  899.  
  900. OOP getCurFileName()
  901. {
  902.   char        *fullFileName;
  903.  
  904.   if (inStream && inStream->type == fileStreamType) {
  905.     if (inStream->fileNameOOP == nilOOP) {
  906.       if (strcmp(inStream->fileName, "stdin") == 0) {
  907.     fullFileName = strdup(inStream->fileName);
  908.       } else {
  909.     fullFileName = getFullFileName(inStream->fileName);
  910.       }
  911.       inStream->fileNameOOP = stringNew(fullFileName);
  912.     }
  913.     return (inStream->fileNameOOP);
  914.   } else {
  915.     return (nilOOP);
  916.   }
  917. }
  918.  
  919. #ifdef USE_READLINE
  920. OOP getCurReadline()
  921. {
  922.   if (inStream && inStream->type == readlineStreamType) {
  923.     return (stringNew(inStream->st_str.strBase));
  924.   } else {
  925.     return (nilOOP);
  926.   }
  927. }
  928. #endif /* USE_READLINE */
  929.  
  930. int getMethodStartPos()
  931. {
  932.   return (methodStartPos);
  933. }
  934.  
  935. void clearMethodStartPos()
  936. {
  937.   methodStartPos = -1;
  938. }
  939.  
  940. int getCurFilePos()
  941. {
  942. #ifdef USE_CHANGES
  943.   return (ftell(changeStr));
  944. #else 
  945.   if (inStream && inStream->type == fileStreamType) {
  946.     return (ftell(inStream->st_file) + inStream->fileOffset);
  947.   } else {
  948.     return (-1);
  949.   }
  950. #endif /* USE_CHANGES */
  951. }
  952.  
  953. void initChangesStream()
  954. {
  955. #ifdef USE_CHANGES
  956.   /* ??? Should this be in the user's home directory, or is there some
  957.      better place for this to be?  Maybe in user's home if there is one,
  958.      else in some canonical place (?).  Canonical place approach seems to
  959.      lose in that canonical places are often write-protected, which is ok
  960.      for extant changes (like in a saved system), but which need to live
  961.      in the user's home area for new changes.  Again, the notion of "the"
  962.      user's home area is kind of random if there are multiple snapshots,
  963.      since each snapshot should be associated with a particular changes
  964.      file for which it is relevant.
  965.      */
  966.   changeStr = fopen(CHANGE_FILE_NAME, "a");
  967. #endif /* USE_CHANGES */
  968. }
  969.  
  970. /*
  971.  *    void resetChangesFile()
  972.  *
  973.  * Description
  974.  *
  975.  *    Resets the changes file to be zero length.
  976.  *
  977.  */
  978. void resetChangesFile()
  979. {
  980. #ifdef USE_CHANGES
  981.   truncate(CHANGE_FILE_NAME, 0);
  982. #endif /* USE_CHANGES */
  983. }
  984.  
  985. static int nextChar()
  986. {
  987.   register int        ic;
  988.  
  989.   if (inStream->pushedBackCount > 0) {
  990.     ic = inStream->pushedBackChars[--inStream->pushedBackCount];
  991.     return (ic);
  992.   } else {
  993.     if (inStream->column == 0 && inStream->prompt) {
  994.       if (emacsProcess) {
  995.     printf("%c", EMACS_PROCESS_MARKER);
  996.       }
  997.       printf("st> ");
  998.     }
  999.     ic = myGetC(inStream);
  1000.  
  1001. #ifdef USE_CHANGES
  1002.     if (ic == EOF) {
  1003.       fflush(changeStr);
  1004.     } else {
  1005.       fputc(ic, changeStr);
  1006.     }
  1007. #endif
  1008.  
  1009.     /* byteAddr++; */
  1010.     if (ic == '\n') {        /* a new line that was not pushed back */
  1011.       inStream->line++;
  1012.       inStream->column = 0;
  1013.     } else {
  1014.       inStream->column++;
  1015.     }
  1016.     return (ic);
  1017.   }
  1018. }
  1019.  
  1020. /*
  1021.  *    static void unreadChar(ic)
  1022.  *
  1023.  * Description
  1024.  *
  1025.  *    Push character 'ic' back into the input queue.  Allows for two
  1026.  *    character pushback currently.  This solves the problem of lexing 3. and
  1027.  *    then finding out that what we should have lexed was 3 followed by . as
  1028.  *    a statement terminator.
  1029.  *
  1030.  * Inputs
  1031.  *
  1032.  *    ic    : character to push back into the input stream.
  1033.  *
  1034.  */
  1035. static void unreadChar(ic)
  1036. int    ic;
  1037. {
  1038.   inStream->pushedBackChars[inStream->pushedBackCount++] = ic;
  1039. }
  1040.  
  1041.  
  1042.  
  1043. /***********************************************************************
  1044.  *
  1045.  *    Generic "stream" interface.  A stream is an abstraction for input and
  1046.  *    output.  It is most like common lisp streams.  Basically, these streams
  1047.  *    provide transparent reading from either a Smalltalk string, or a UNIX
  1048.  *    file.  They stack, and the previous stream can be restored by doing a
  1049.  *    "popStream" which optionally "closes" the current stream and goes back 
  1050.  *    to using the previous stream.
  1051.  *
  1052.  *     The `readline()' interface:
  1053.  *
  1054.  *         The behavior is like the Smalltalk-String interface.
  1055.  *         The end-of-string or a NULL strBase-pointer decides
  1056.  *         to read in a new line. The prompt is still shown by
  1057.  *         the readline() call.
  1058.  *         
  1059.  ***********************************************************************/
  1060.  
  1061. void popStream(closeIt)
  1062. Boolean closeIt;
  1063. {
  1064.   Stream    oldStream;
  1065.  
  1066.   oldStream = inStream;
  1067.   inStream = inStream->prevStream;
  1068.  
  1069.   if (closeIt && oldStream) {
  1070.     myClose(oldStream);
  1071.     free(oldStream);
  1072.   }
  1073. }
  1074.  
  1075. void pushUNIXFile(file, fileName)
  1076. FILE    *file;
  1077. char    *fileName;
  1078. {
  1079.   Stream    newStream;
  1080.  
  1081.   newStream = pushNewStream(fileStreamType);
  1082.  
  1083.   newStream->st_file = file;
  1084.   newStream->fileName = fileName;
  1085.   newStream->prompt = isatty(fileno(file));
  1086. }
  1087.  
  1088. void pushSmalltalkString(stringOOP)
  1089. OOP    stringOOP;
  1090. {
  1091.   Stream    newStream;
  1092.  
  1093.   newStream = pushNewStream(stringStreamType);
  1094.  
  1095.   newStream->st_str.strBase = (char *)toCString(stringOOP);
  1096.   newStream->st_str.str = newStream->st_str.strBase;
  1097.   newStream->fileName = "a Smalltalk string";
  1098.   newStream->prompt = false;
  1099. }
  1100.  
  1101. void pushCString(string)
  1102. char    *string;
  1103. {
  1104.   Stream    newStream;
  1105.  
  1106.   newStream = pushNewStream(stringStreamType);
  1107.  
  1108.   newStream->st_str.strBase = string;
  1109.   newStream->st_str.str = newStream->st_str.strBase;
  1110.   newStream->fileName = "a C string";
  1111.   newStream->prompt = false;
  1112. }
  1113.  
  1114. #ifdef USE_READLINE
  1115. void pushReadlineString()
  1116. {
  1117.   Stream    newStream;
  1118.  
  1119.   newStream = pushNewStream(readlineStreamType);
  1120.  
  1121.   newStream->st_str.strBase = 0;    /* force readline() but no free() */
  1122.   newStream->st_str.str = 0;
  1123.   newStream->fileName = "a Readline string";
  1124.   newStream->prompt = false;        /* prompt is shown by readline() */
  1125. }
  1126. #endif /* USE_READLINE */
  1127.  
  1128. static Stream pushNewStream(type)
  1129. StreamType type;
  1130. {
  1131.   Stream    newStream;
  1132.  
  1133.   newStream = (Stream)malloc(sizeof(struct StreamStruct));
  1134.  
  1135.   newStream->pushedBackCount = 0;
  1136.   newStream->line = 1;
  1137.   newStream->column = 0;
  1138.   newStream->fileOffset = 0;
  1139.   newStream->type = type;
  1140.   newStream->fileNameOOP = nilOOP;
  1141.   newStream->prevStream = inStream;
  1142.   inStream = newStream;
  1143.  
  1144.   return (newStream);
  1145. }
  1146.  
  1147.  
  1148. /*
  1149.  *    void setStreamInfo(line, fileName, fileOffset)
  1150.  *
  1151.  * Description
  1152.  *
  1153.  *    This function overrides the file type information for the current stream.
  1154.  *    It is typically used by fileIn type methods when filing in a subsection
  1155.  *    of a real file via a temporary file what the real source of the text
  1156.  *    is.
  1157.  *
  1158.  * Inputs
  1159.  *
  1160.  *    line  : line number that the input starts on
  1161.  *    fileName: 
  1162.  *        The name of the file, only used for file type streams.
  1163.  *    fileOffset: 
  1164.  *        The byte offset from the start of the actual file where this
  1165.  *        given portion of code starts.
  1166.  *
  1167.  */
  1168. void setStreamInfo(line, fileName, fileOffset)
  1169. int    line, fileOffset;
  1170. char    *fileName;
  1171. {
  1172.   inStream->line = line;
  1173.   if (inStream->type == fileStreamType) {
  1174.     inStream->fileName = fileName;
  1175.     inStream->fileOffset = fileOffset;
  1176.   }
  1177. }
  1178.  
  1179. static int myGetC(stream)
  1180. Stream    stream;
  1181. {
  1182.   int        ic;
  1183.  
  1184.   if (stream->type == stringStreamType) {
  1185.     ic = *stream->st_str.str++;
  1186.     return ((ic == '\0') ? EOF : ic);
  1187.   } else if (stream->type == fileStreamType) {
  1188.     return (getc(stream->st_file));
  1189. #ifdef USE_READLINE
  1190.   } else if (stream->type == readlineStreamType) {
  1191.     char *r_line;
  1192.     extern char *realloc();
  1193.     int r_len;
  1194.  
  1195.     if (stream->st_str.strBase) {
  1196.       ic = *stream->st_str.str++;
  1197.     } else {
  1198.       ic = '\0';
  1199.     }
  1200.  
  1201.     if (ic == '\0') {
  1202.       if(stream->st_str.strBase) {
  1203.     free(stream->st_str.strBase);
  1204.     stream->st_str.strBase = 0;
  1205.       }
  1206.       r_line = readline("st> ");
  1207.       if (!r_line) {
  1208.     /*
  1209.      * return value of NULL indicates EOF:
  1210.      */
  1211.     return EOF;
  1212.       }
  1213.       if (*r_line) {
  1214.     /*
  1215.      * add only non-empty lines.
  1216.      */
  1217.     add_history(r_line);
  1218.       }
  1219.       /*
  1220.        * tack on the newline, not returned by readline() :
  1221.        */
  1222.       r_len =  strlen(r_line);
  1223.       r_line = realloc(r_line, (unsigned) (r_len + 2));
  1224.       if (!r_line) {
  1225.     errorf("Out of memory, reallocating linebuffer space");
  1226.     stream->st_str.str = stream->st_str.strBase = 0;
  1227.     ic = '\n';            /* return a newline ... */
  1228.       } else {
  1229.     r_line[r_len] = '\n';
  1230.     r_line[r_len+1] = '\0';
  1231.     stream->st_str.str = stream->st_str.strBase = r_line;
  1232.     ic = *stream->st_str.str++;
  1233.       }
  1234.     }
  1235.     return (ic);
  1236. #endif /* USE_READLINE */
  1237.   } else {
  1238.     errorf("Bad stream type passed to myGetC");
  1239.     hadError = true;
  1240.   }
  1241. }
  1242.  
  1243. #ifdef old_code /* Sat Mar 23 21:00:38 1991 */
  1244. /**/static void myUngetC(ic, stream)
  1245. /**/int    ic;
  1246. /**/Stream    stream;
  1247. /**/{
  1248. /**/  if (stream->type == stringStreamType) {
  1249. /**/    stream->st_str.str--;
  1250. /**/  } else if (stream->type == fileStreamType) {
  1251. /**/    ungetc(ic, stream->st_file);
  1252. /**/#ifdef USE_READLINE
  1253. /**/  } else if (stream->type == readlineStreamType) {
  1254. /**/    if (stream->st_str.str > stream->st_str.strBase) {
  1255. /**/      *--stream->st_str.str = ic;
  1256. /**/    } else {
  1257. /**/      errorf("Cannot unget character to readline stream");
  1258. /**/    }
  1259. /**/#endif /* USE_READLINE */
  1260. /**/  } else {
  1261. /**/    errorf("Bad stream type passed to myUngetC");
  1262. /**/    hadError = true;
  1263. /**/  }
  1264. /**/}
  1265. #endif /* old_code Sat Mar 23 21:00:38 1991 */
  1266.  
  1267. static void myClose(stream)
  1268. Stream    stream;
  1269. {
  1270.   if (stream->type == stringStreamType) {
  1271.     free(stream->st_str.strBase);
  1272.   } else if (stream->type == fileStreamType) {
  1273.     fclose(stream->st_file);
  1274. #ifdef USE_READLINE
  1275.   } else if (stream->type == readlineStreamType) {
  1276.     if (stream->st_str.strBase) {
  1277.       free(stream->st_str.strBase);
  1278.       stream->st_str.strBase = 0;
  1279.     }
  1280. #endif /* USE_READLINE */
  1281.   } else {
  1282.     errorf("Bad stream type passed to myClose");
  1283.     hadError = true;
  1284.   }
  1285. }
  1286.  
  1287.  
  1288. #ifdef USE_READLINE
  1289.  
  1290. void initializeReadline()
  1291. {
  1292.   /* Allow conditional parsing of the ~/.inputrc file. */
  1293.   rl_readline_name = "Smalltalk";
  1294. }
  1295.      
  1296. #endif /* USE_READLINE */
  1297.